#include "STrainer.h"
#include "ICheat.h"

namespace TLib
{	
	template<class T>
	inline T MAX(T a, T b)
	{ return a > b ? a : b; }

	template<class T>
	inline T MIN(T a, T b)
	{ return (a) > (b) ? (b) : (a); }
	unsigned int getVScrollPos(HWND parent)
	{
		SCROLLINFO si;
		si.fMask  = SIF_ALL;
		si.cbSize = sizeof(SCROLLINFO);
		GetScrollInfo(parent, SB_VERT, &si);
		return si.nPos;
	}
	unsigned int getHScrollPos(HWND parent)
	{
		SCROLLINFO si;
		si.fMask  = SIF_ALL;
		si.cbSize = sizeof(SCROLLINFO);
		GetScrollInfo(parent, SB_HORZ, &si);
		return si.nPos;
	}

	TRAINER_LIB_API BOOL CALLBACK STrainer::tr_cheatDlgProc(HWND hDlg, UINT message, 
		WPARAM wParam, LPARAM lParam)
	{
		static STrainer* app;
		static HINSTANCE hInstance;
		static std::vector<HWND> bToggle;
		static unsigned int bH, bW, cyChar, cxChar, sep, wndW, wndH;
		static SCROLLINFO si;
		static bool hscBottom, vscBottom;

		switch (message)
		{
		case WM_INITDIALOG:
			{
				cxChar = LOWORD(GetDialogBaseUnits());
				cyChar = HIWORD(GetDialogBaseUnits());
				hscBottom = vscBottom = false;

				app = ((STrainer*)lParam);
				hInstance = app->getTrainerHInstance();
				bToggle.resize(app->getNumberOfCheats());

				wndW = 354;
				wndH = 285;
				bH  = 25;
				bW  = 50;
				sep = 8;

				unsigned int h_siNMax = 0, cname = 0, chotkeystr = 0;
				for (unsigned int i = 0; i < app->getNumberOfCheats(); i++)
				{
					bToggle[i] = CreateWindowA
						(
						TEXT("button"), 
						"Toggle", 
						WS_VISIBLE | WS_CHILD, 
						(4*cxChar),
						i*bH, 
						bW, 
						bH, 
						hDlg, 
						(HMENU)i, 
						(HINSTANCE)hInstance, 
						0
						);
					cname = (app->getCheat(i)->getName().size()*cxChar);
					chotkeystr = (app->getCheat(i)->getHotKey()->getHotkeyString().size()*cxChar);

					h_siNMax = MAX(h_siNMax, 
						sep+bW+cname+(3*cxChar)+chotkeystr);
				}

				si.cbSize = sizeof(SCROLLINFO);
				si.fMask  = SIF_RANGE | SIF_PAGE ;
				si.nMax   = app->getNumberOfCheats()*bH;
				si.nMin   = 0;
				si.nPage  = wndH;
				SetScrollInfo(hDlg, SB_VERT, &si, TRUE);

				si.nMax   = h_siNMax;
				si.nPage  = wndW;
				SetScrollInfo(hDlg, SB_HORZ, &si, TRUE);

				return TRUE;
			}
		case WM_SIZE:
			{
				//The low-order word of lParam specifies the new width of the client area.
				//The high-order word of lParam specifies the new height of the client area.

				for (unsigned int i = 0; i < app->getNumberOfCheats(); i++)
				{
					MoveWindow
						(
						bToggle[i],
						(4*cxChar) - getHScrollPos(hDlg),
						i*bH - getVScrollPos(hDlg),
						bW,
						bH,
						FALSE
						);
				}

				wndW = LOWORD(lParam);
				wndH = HIWORD(lParam);

				si.fMask = SIF_PAGE;
				si.nPage = wndH;
				SetScrollInfo(hDlg, SB_VERT, &si, FALSE);

				si.nPage = wndW;
				SetScrollInfo(hDlg, SB_HORZ, &si, FALSE);


				unsigned int h_siNMax = 0, cname = 0, chotkeystr;
				for (unsigned int i = 0; i < app->getNumberOfCheats(); i++)
				{
					cname = (app->getCheat(i)->getName().size()*cxChar);
					chotkeystr = (app->getCheat(i)->getHotKey()->getHotkeyString().size()*cxChar);
					h_siNMax = MAX(h_siNMax, 
						sep+bW+cname+(3*cxChar)+chotkeystr);
				}
				si.fMask  = SIF_RANGE | SIF_PAGE ;
				si.nMax   = h_siNMax;
				si.nPage  = wndW;
				SetScrollInfo(hDlg, SB_HORZ, &si, TRUE);

				InvalidateRect(hDlg, NULL, TRUE);

				return TRUE;
			}
		case WM_HSCROLL:
		 {
			 si.fMask  = SIF_ALL;
			 GetScrollInfo (hDlg, SB_HORZ, &si);

			 unsigned int oldPos = si.nPos;

			 switch (LOWORD (wParam))
			 {
				 // Scroll box has reached top
			 case SB_TOP:
				 si.nPos = si.nMin;
				 break;

				 // Scroll box has reached bottom
			 case SB_BOTTOM:
				 hscBottom = true;
				 si.nPos = si.nMax;
				 break;

				 // Scrolls one line up
			 case SB_LINEUP:
				 si.nPos -= bH;
				 break;

				 // Scrolls one line down
			 case SB_LINEDOWN:
				 si.nPos += bH;
				 break;

				 // Scrolls one page up
			 case SB_PAGEUP:
				 si.nPos -= si.nPage;
				 break;

				 // Scrolls one page down
			 case SB_PAGEDOWN:
				 si.nPos += si.nPage;
				 break;

				 // The user is dragging the scroll box. This message is sent repeatedly 
				 // until the user releases the mouse button. The nPos parameter indicates 
				 // the position that the scroll box has been dragged to.
			 case SB_THUMBTRACK:
				 si.nPos = si.nTrackPos;
				 break;

			 default:
				 break;         
			 }

			 si.fMask = SIF_POS;
			 SetScrollInfo(hDlg, SB_HORZ, &si, TRUE);
			 GetScrollInfo(hDlg, SB_HORZ, &si);

			 if (si.nPos != oldPos)
			 {
				 ScrollWindowEx
					 (
					 hDlg,
					 oldPos - si.nPos,
					 0,
					 NULL,
					 NULL,
					 NULL,
					 NULL,
					 SW_SCROLLCHILDREN
					 );
				 InvalidateRect(hDlg, NULL, TRUE);
			 }
			 return TRUE;
		 }
		case WM_VSCROLL:
		 {
			 si.fMask  = SIF_ALL;
			 GetScrollInfo (hDlg, SB_VERT, &si);

			 unsigned int oldPos = si.nPos;

			 switch (LOWORD (wParam))
			 {
				 // Scroll box has reached top
			 case SB_TOP:
				 si.nPos = si.nMin;
				 break;

				 // Scroll box has reached bottom
			 case SB_BOTTOM:
				 vscBottom = true;
				 si.nPos = si.nMax;
				 break;

				 // Scrolls one line up
			 case SB_LINEUP:
				 si.nPos -= bH;
				 break;

				 // Scrolls one line down
			 case SB_LINEDOWN:
				 si.nPos += bH;
				 break;

				 // Scrolls one page up
			 case SB_PAGEUP:
				 si.nPos -= si.nPage;
				 break;

				 // Scrolls one page down
			 case SB_PAGEDOWN:
				 si.nPos += si.nPage;
				 break;

				 // The user is dragging the scroll box. This message is sent repeatedly 
				 // until the user releases the mouse button. The nPos parameter indicates 
				 // the position that the scroll box has been dragged to.
			 case SB_THUMBTRACK:
				 si.nPos = si.nTrackPos;
				 break;

			 default:
				 break;         
			 }

			 si.fMask = SIF_POS;
			 SetScrollInfo(hDlg, SB_VERT, &si, TRUE);
			 GetScrollInfo(hDlg, SB_VERT, &si);

			 if (si.nPos != oldPos)
			 {
				 ScrollWindowEx
					 (
					 hDlg,
					 0,
					 oldPos - si.nPos,
					 NULL,
					 NULL,
					 NULL,
					 NULL,
					 SW_SCROLLCHILDREN
					 );
				 InvalidateRect(hDlg, NULL, TRUE);
			 }
			 return TRUE;
		 }
		case WM_COMMAND:
			{
				// The ID of the control is the low word of wParam.
				unsigned int id = LOWORD(wParam);

				if (id >= 0 && id < app->getNumberOfCheats())
				{
					HANDLE hProcess = app->GetGameProcessHandle();
					if (hProcess == NULL)
						return TRUE;

					if (app->getCheat(id)->isEnabled())
					{
						app->getCheat(id)->Off(hProcess);
						InvalidateRect(hDlg, NULL, TRUE);
					}
					else
					{
						app->getCheat(id)->On(hProcess);
						InvalidateRect(hDlg, NULL, TRUE);
					}
					CloseHandle(hProcess);
				}
				return TRUE;
			}
		case WM_PAINT:
			{
				HDC         hdc;
				PAINTSTRUCT ps;
				RECT        rect;
				//HFONT       font;

				GetClientRect (hDlg, &rect);
				hdc = BeginPaint (hDlg, &ps);

				//font = CreateFont(cyChar, cxChar, 0, 0,
				//	FW_NORMAL, FALSE, FALSE, FALSE,
				//	ANSI_CHARSET, OUT_DEFAULT_PRECIS,
				//	CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
				//	DEFAULT_PITCH | FF_ROMAN,
				//	"Times New Roman");

				//SelectObject(hdc, font);

				SetBkMode(hdc, TRANSPARENT);
				
				std::string hotkey;
				for (unsigned int i = 0; i < app->getNumberOfCheats(); i++)
				{
					SetTextColor
						(
						hdc, 
						RGB(app->getCheat(i)->isEnabled() ? 0 : 255, app->getCheat(i)->isEnabled() ? 255 : 0, 0)
						);
					TextOutA
						(
						hdc, 
						0 - getHScrollPos(hDlg), 
						i*bH + (bH/4) - getVScrollPos(hDlg),
						app->getCheat(i)->isEnabled() ? "ON " : "OFF ", 
						app->getCheat(i)->isEnabled() ? 3 : 4
						);

					SetTextColor(hdc, RGB(255,0,0));
					TextOutA
						(
						hdc,
						bW + (5*cxChar) - getHScrollPos(hDlg),
						i*bH + (bH/4) - getVScrollPos(hDlg),
						app->getCheat(i)->getHotKey()->getHotkeyString().c_str(),
						app->getCheat(i)->getHotKey()->getHotkeyString().size()
						);

					SetTextColor(hdc, RGB(0,0,0));
					TextOutA
						(
						hdc, 
						bW + (5*cxChar) - getHScrollPos(hDlg) + (app->getCheat(i)->getHotKey()->getHotkeyString().size()*cxChar), 
						i*bH + (bH/4) - getVScrollPos(hDlg), 
						app->getCheat(i)->getName().c_str(),
						app->getCheat(i)->getName().size()
						);

				}

				EndPaint (hDlg, &ps);
				//DeleteObject(font);

				return TRUE;

			}
		case WM_CLOSE:
			{
				EndDialog(hDlg, 0);
				return TRUE;
			}
		}
		return FALSE;
	}

}// TLib Namespace